export default class Caret {
  constructor(el: HTMLElement) {
    this.init(el)
  }

  // root element for editor
  private el: HTMLElement

  // init editor
  private init(el: HTMLElement) {
    if (!el || el instanceof HTMLElement === false) throw new Error(`[Error] arguments[0] is required an HTMLElement but got ${el}`)

    if (!el.isContentEditable) el.setAttribute('contenteditable', 'true')

    this.el = el

    return this.el
  }

  // get current position of selection
  public getPosition(el?: HTMLElement): number {
    el = el || this.el!

    const selection = window.getSelection()

    if (!selection) return -1

    if (!el) return selection.rangeCount - 1

    if (!selection.anchorNode || !el.contains(selection.anchorNode)) {
      selection.empty()
      selection.selectAllChildren(el)
      selection.collapseToEnd()

      return this.getPosition()
    }

    return selection.rangeCount - 1
  }

  // set selection into elements‘s start tag position
  public setPositionToInnerStart(el: HTMLElement) {
    if (!el) return

    const selection = window.getSelection()

    selection?.selectAllChildren(el)
    selection?.collapseToStart()
  }

  // set selection into elements‘s end tag position
  public setPositionToInnerEnd(el: HTMLElement) {
    if (!el) return

    const selection = window.getSelection()

    selection?.selectAllChildren(el)
    selection?.collapseToEnd()
  }

  // set selection front of elements‘s start tag position
  public setPositionToOuterStart(el: HTMLElement) {
    if (!el) return

    const selection = window.getSelection()

    const parent = el.parentNode

    selection?.collapse(parent, 0)
  }

  // set selection behind of elements‘s end tag position
  public setPositionToOuterEnd(el: HTMLElement) {
    if (!el) return

    const selection = window.getSelection()

    const parent = el.parentNode

    const index = Array.prototype.indexOf.call(parent?.childNodes, el) + 1

    selection?.collapse(parent, index)
  }

  // insert element to current position
  public insertNode(el: HTMLElement) {
    if (!el) return

    if (el.nodeType === el.TEXT_NODE) {
      const span = document.createElement('span')

      span.innerHTML = (el as HTMLElement & Text).data

      el = span
    }

    const position = this.getPosition()

    const selection = window.getSelection()

    selection?.getRangeAt(position).insertNode(el)

    this.setPositionToOuterEnd(el)

    el.scrollIntoView({ behavior: 'smooth', block: 'center' })
  }
}